package com.atjava.nebula.core.util.bean;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.File;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.MalformedURLException;
import java.net.URL;
import java.sql.Date;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 可以添加动态属性的类实现(本类的子类可以定义自己的静态属性，并自动获得动态属性的特性)<br>
 * 
 * (实现说明：本实现类通过Java反射机制获得子类或目标对象所声明的(静态)属性信息)
 * (注：基本思想及部分代码来源于<code>org.apache.commons.chain.impl.ContextBase</code>和
 * <code>org.apache.commons.beanutils.ConvertUtilsBean</code>)
 * 
 * @author kyo
 */
public class DynamicBeanImpl implements DynamicBean, Map {
	private static final long serialVersionUID = -5970500307239586606L;
	//
	private static final boolean debug = false;
	//
	private Object 	baseObject; 	//基对象，其他静态属性来自于它
	private Map<String, Class<?>> 	allPropTypeMap = new HashMap<String, Class<?>>();	//存放所有属性名称和类型信息
	private Map<String, Object> 	extPropValueMap = new HashMap<String, Object>();	//存放扩展(已声明)属性的值信息
	private Map<String, Object> 	tempPropValueMap = new HashMap<String, Object>();	//存放临时(未声明)属性的值信息
	private transient Map<String, PropertyDescriptor> descriptors;						//缓存静态属性描述符,以提高性能

	public DynamicBeanImpl() {
		baseObject = this;
		initialize(getClass());
	}
	
	public DynamicBeanImpl(Object baseObject){
		this.baseObject = baseObject;
		initialize(baseObject.getClass());
	}

	private void initialize(Class clazz) {
		// 获得目标被扩展类的属性描述
		PropertyDescriptor[] pds = null;
		try {
			pds = Introspector.getBeanInfo(clazz).getPropertyDescriptors();
		} catch (IntrospectionException e) {
			pds = new PropertyDescriptor[0]; // Should never happen
		}

		// 初始化内部对象
		descriptors = new HashMap<String, PropertyDescriptor>();
		for(int i = 0; i < pds.length; i++) {
			// 跳过Object的getClass()和Map、Collection的isEmpty()方法
			if (pds[i].getName().equals("class") || pds[i].getName().equals("empty")) continue;
			//
			allPropTypeMap.put(pds[i].getName(), pds[i].getPropertyType());
			descriptors.put(pds[i].getName(), pds[i]);
		}
	}

	/**+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	 * 						DynamicBean接口方法区
	 **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
	/**
	 * 获得指定属性的值(DynamicBean接口方法)
	 */
	public Object get(String propName) {
		if (propName == null)
			throw new IllegalArgumentException("porpName is NULL!");
		
		if(allPropTypeMap.get(propName) == null) return tempPropValueMap.get(propName);	//未声明的属性

		PropertyDescriptor descriptor = getPropertyDescriptor(propName);
		if (descriptor != null) {
			return (descriptor.getReadMethod() != null) ? readProperty(descriptor) : null;
		}

		return extPropValueMap.get(propName);
	}

	/**
	 * 设置指定属性的值(DynamicBean接口方法)<br>
	 * (注：对于已声明的属性，支持到目标类型的转换。当前可以处理从<code>String</code>到基本类型的双向转换。
	 * 其他暂不支持的复杂类型，需要用户自己保证值对象和目标类型一致。如果转换失败，将抛出运行时异常。)
	 * <ul>当前支持的基本类型包括：
	 * <li>boolean and java.lang.Boolean</li>
	 * <li>byte and java.lang.Byte</li>
	 * <li>char and java.lang.Character</li>
	 * <li>double and java.lang.Double</li>
	 * <li>float and java.lang.Float</li>
	 * <li>int and java.lang.Integer</li>
	 * <li>long and java.lang.Long</li>
	 * <li>short and java.lang.Short</li>
	 * <li>java.math.BigDecimal</li>
	 * <li>java.math.BigInteger</li>
	 * <li>java.lang.String</li>
	 * <li>java.io.File</li>
	 * <li>java.net.URL</li>
	 * <li>java.sql.Date</li>
	 * <li>java.sql.Time</li>
	 * <li>java.sql.Timestamp</li>
	 * </ul>
	 */
	public void set(String propName, Object value) {
		if(propName == null) 
			throw new IllegalArgumentException("porpName is NULL!");
		
		if(allPropTypeMap.get(propName) == null) tempPropValueMap.put(propName, value);	//未声明的属性

		PropertyDescriptor descriptor = getPropertyDescriptor(propName);
		if (descriptor != null) {//是静态属性
			writeProperty(descriptor, convert(value, allPropTypeMap.get(propName)));
			return;
		}
		//是动态属性
		extPropValueMap.put(propName, convert(value, allPropTypeMap.get(propName)));
	}
	
	/**
	 * 声明属性【名称，类型】，如果参数propType为NULL,则取消相应的声明；静态属性无需(并且不能)重复声明
	 */
	public void declare(String propName, Class propType) {
		if(propName == null) 
			throw new IllegalArgumentException("porpName is NULL!");		
		if(getPropertyDescriptor(propName) != null)
			throw new IllegalArgumentException("Can't change static property["+propName+"]'s data type.");
		
	    if(debug) System.out.println("from DynamicBeanImpl.declare(): property["+propName+"] is declared.");
	    
		if(propType == null) allPropTypeMap.remove(propName);
		else allPropTypeMap.put(propName, propType);
	}
	
	/**
	 * 描述指定类型的所有属性，格式：Map.Entry&lt;propName,propType>
	 */
	public Map<String,Class> describe(int classify) {		
		if(descriptors == null) initialize(this.baseObject.getClass());	//保证 descriptors Map 已被初始化
		
		if(classify == CLY_ALL_PROPERTIES) {
			return new HashMap<String, Class>(allPropTypeMap);
		}
		if(classify == CLY_STATIC_PROPS) {
			Map<String,Class> staticProps = new HashMap<String, Class>();
			for(String propName : descriptors.keySet()) 
				staticProps.put(propName, allPropTypeMap.get(propName));
			return staticProps;
		}
		if(classify == CLY_DYNAMIC_PROPS){
			Map<String,Class> dynamicProps = new HashMap<String, Class>(allPropTypeMap);
			for(String propName : descriptors.keySet()) dynamicProps.remove(propName);
			return dynamicProps;
		}
		
		return new HashMap<String, Class>();
	}

	private PropertyDescriptor getPropertyDescriptor(String propName) {
		if(descriptors == null) initialize(this.baseObject.getClass());
		return (PropertyDescriptor) descriptors.get(propName.trim());
	}

	/**+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	 * 								Map接口方法区
	 **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
	/**
	 * 获得指定属性的值(Map接口方法)
	 */
	public Object get(Object propName){
		if (propName == null)
			throw new IllegalArgumentException("key is NULL!");
		
		return this.get(propName.toString());
	}
	
	/**
	 * 设置指定属性的值(Map接口方法)
	 */
	public Object put(Object propName, Object value){
		if(propName == null) 
			throw new IllegalArgumentException("key is NULL!");
		
		Object old = get(propName);
		this.set(propName.toString(), value);
		return old;
	}

	/**
	 * 批量设置指定属性的值
	 */
	public void putAll(Map entries) {
		if(entries == null) return;
		for(Object propName : entries.keySet()){
			put(propName, entries.get(propName));
		}
	}
	
	/**
	 * 移除指定属性的值
	 */
	public Object remove(Object propName) {
		Object oldValue = get(propName);
		put(propName, null);
		return oldValue;
	}
	
	/**
	 * 清空所有(已声明)属性的值
	 */
	public void clear() {
		for(Object propName : keySet()){
			put(propName, null);
		}
	}
	
	/**
	 * 是否包含指定(已声明)属性名
	 */
	public boolean containsKey(Object propName) {
		for(Object name : keySet()){
			if(name.equals(propName)) return true;
		}
		return false;
	}
	
	/**
	 * 是否包含指定(已声明)属性值
	 */
	public boolean containsValue(Object value) {
		for(Object propName : keySet()){
			if(get(propName) == null && value == null) return true;
			if(get(propName) != null && get(propName).equals(value)) return true;
		}
		return false;
	}
	
	/**
	 * 获得所有(已声明)属性名
	 */
	public Set keySet(){
		return allPropTypeMap.keySet();
	}
    
	/**
	 * 获得所有(已声明)属性值
	 */
	public Collection values() {
		List<Object> result = new ArrayList<Object>();
		for(Object key : keySet()){
			result.add(get(key));
		}
		return result;
	}
	
	/**
	 * 获得所有(已声明)属性名值集
	 */
	@SuppressWarnings("unchecked")
	public Set entrySet() {
		Map tempMap= new HashMap();
		for(Object key : keySet()){
			tempMap.put(key, get(key));
		}
		return tempMap.entrySet();
	}
	
	/**
	 * 是否所有(已声明)属性值均不为NULL
	 */
	public boolean isEmpty() {
		for(Object key : keySet()){
			if(get(key) != null) return false;
		}
		return true;
	}
	
	/**
	 * 所有(已声明)属性个数
	 */
	public int size() {
		return keySet().size();
	}

	/**+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
	 * 								辅助方法区
	 **++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
	/**
	 * 读属性值
	 * @param descriptor
	 */
	private Object readProperty(PropertyDescriptor descriptor) {
		try {
			Method method = descriptor.getReadMethod();
			if (method == null)
				throw new UnsupportedOperationException("Property '" + descriptor.getName()
						+ "' is not readable");

			return (method.invoke(baseObject, new Object[0]));
		} catch (Exception e) {
			throw new RuntimeException("Exception reading property '" + descriptor.getName()
					+ "': " + e.getMessage());
		}
	}

	/**
	 * 写属性值
	 * @param descriptor
	 * @param value
	 */
	private void writeProperty(PropertyDescriptor descriptor, Object value) {
		try {
			Method method = descriptor.getWriteMethod();
			if (method == null)
				throw new UnsupportedOperationException("Property '" + descriptor.getName()
						+ "' is not writeable");

			method.invoke(baseObject, new Object[] { value });
		} catch (Exception e) {
			throw new RuntimeException("Exception writing property '" + descriptor.getName()
					+ "': " + e.getMessage());
		}
	}

	/**
	 * 把传入对象转换成目标类型
	 * @param object
	 * @param toType
	 * @return
	 */
	private Object convert(Object object, Class<?> toType) {
		if(toType == null) return object;
        if(object == null) return (toType.isPrimitive()) ? convertNullToPrimitive(toType) : null;
        //向上转型或者同一类型，不必处理
        if(toType.isAssignableFrom(object.getClass())) return object;
        //到基本类型的转换
        if(isBasicDataType(toType)) {
        	if(object instanceof String && ((String)object).trim().equals("") && toType.isPrimitive())//空值处理
        		return convertNullToPrimitive(toType);
        	
        	return convertStringToBasic(object, toType);
        }
        
        throw new IllegalArgumentException("Unsupported convertion: "
				+ object.getClass().getName() + "-->" + toType.getName());
	}
	
	/**
	 * 判断传入类型是否是基本类型
	 */
	private boolean isBasicDataType(Class toType){
		if(toType == boolean.class || toType == Boolean.class) return true;
		if(toType == char.class || toType == Character.class) return true;
		if(toType == byte.class || toType == Byte.class) return true;
		if(toType == short.class || toType == Short.class) return true;
		if(toType == int.class || toType == Integer.class) return true;
		if(toType == long.class || toType == Long.class) return true;
		if(toType == float.class || toType == Float.class) return true;
		if(toType == double.class || toType == Double.class) return true;		
		if(toType == String.class) return true;
		if(toType == BigDecimal.class || toType == BigInteger.class) return true;		
		if(toType == Date.class || toType == Time.class || toType == Timestamp.class) return true;
		if(toType == File.class) return true;
		if(toType == URL.class) return true;		
		return false;
	}

	/**
	 * 把NULL转换成主类型(因为主类型不能为NULL)
	 * @param toType
	 * @return
	 */
    private Object convertNullToPrimitive(Class toType) {
		if (toType == boolean.class) return Boolean.FALSE;
		if (toType == char.class) return new Character('\0');
		if (toType == byte.class) return new Byte((byte)0);
		if (toType == short.class) return new Short((short)0);
		if (toType == int.class) return new Integer(0);
		if (toType == long.class) return new Long(0L);
		if (toType == float.class) return new Float(0.0f);
		if (toType == double.class) return new Double(0.0);
		
		return null;
	}

    /**
     * 把当前对象转换成目标(基本)类型
     * @param object
     * @param toType
     * @return
     */
	private Object convertStringToBasic(Object object, Class toType) {
		if(toType == boolean.class || toType == Boolean.class) return Boolean.valueOf(object.toString());
		if(toType == char.class || toType == Character.class) return new Character(object.toString().charAt(0));
		if(toType == byte.class || toType == Byte.class) return new Byte(object.toString());
		if(toType == short.class || toType == Short.class) return new Short(object.toString());
		if(toType == int.class || toType == Integer.class) return new Integer(object.toString());
		if(toType == long.class || toType == Long.class) return new Long(object.toString());
		if(toType == float.class || toType == Float.class) return new Float(object.toString());
		if(toType == double.class || toType == Double.class) return new Double(object.toString());
		if(toType == BigDecimal.class) return new BigDecimal(object.toString());
		if(toType == BigInteger.class) return new BigInteger(object.toString());
		if(toType == String.class) return object.toString();
		if(toType == Date.class) return Date.valueOf(object.toString());
		if(toType == Time.class) return Time.valueOf(object.toString());
		if(toType == Timestamp.class) return Timestamp.valueOf(object.toString());
		if(toType == File.class) return new File(object.toString());
		try {
			if(toType == URL.class) return new URL(object.toString());
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		
		return null;
	}

}
